package server.support;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import server.error.RepeatRegisteException;
import server.error.ServerNotFoundException;
import server.handle.IDispatchHandle;
import server.handle.ServerInboundHandle;
import server.route.IRoute;
import support.RpcInfo;
import util.StringUtil;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;

public class ServerInfoMap implements IServerInfoMap{
    static Logger logger = LoggerFactory.getLogger(ServerInfoMap.class);

    //K:RpcInfo key值，暂时使用:class@method@typeList 格式,例子:com.dqy.OrderServer@del@[String,String,Order]
    Map<String, List<RpcInfo>> rpcInfoMap = new ConcurrentHashMap<>();

    //根据IP+Port 映射 rpcInfpMap的key，方便服务下线删除,
    //简单的说就是把rpcInfoMap 的key存在这里
    Map<String, Set<String>> keyMap = new ConcurrentHashMap<>();

    private String generateKey(RpcInfo info){
        StringBuilder builder = new StringBuilder();
        builder.append(StringUtil.getSimpleName(info.getClassName()));
        builder.append("@");
        builder.append(info.getMethodName());
        builder.append("@[");
        if(info.getMethodType().length>0) {
            for (Class c : info.getMethodType()) {
                builder.append(c.getSimpleName());
                builder.append(",");
            }
        }
        builder.append("]");
        String key = builder.toString();
        logger.info("key:{}",key);
        return key;
    }

    //0，通过  1，重复  -1不通过，数据缺失
    private int checkAddr(RpcInfo info,List<RpcInfo> list){
        String host = info.getHost();
        int port = info.getPort();
        int resi = 0;
        if(StringUtil.strIsEmpty(host) || port<1){
            logger.error("主机名端口不可为空...");
            resi=-1;
        }
        if(list!=null)
            for (RpcInfo r : list) {
                if (r.getPort() == port && host.equals(r.getHost())) //表示重复注册
                    resi = 1;
            }
        return resi;
    }

    private RpcInfo cloneInfo(RpcInfo info){
        assert info!=null;
        try {
            return (RpcInfo) info.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }

    //把生成的元数据map key值当作value，IP:PORT当作key保存起来，方便后续删除
    private void addKeyMap(String value,RpcInfo info){
        String key = info.getHost() + ":" + info.getServerToCenterPort();
        Set<String> set = keyMap.get(key);
        if(set==null){
            set = new CopyOnWriteArraySet<>();
            set.add(value);
            keyMap.put(key,set);
        }else
            set.add(value);
    }

    @Override
    public void registe(RpcInfo info) throws RepeatRegisteException {
        String key = generateKey(info);
        if(checkAddr(info,null)==-1){
            logger.error("非法的注册,IP:PORT为空!...");
            throw new RepeatRegisteException("非法的注册,IP:PORT为空!...");
        }

        List<RpcInfo> rpcInfoList = rpcInfoMap.get(key);
        if(StringUtil.listIsEmpty(rpcInfoList)){
            logger.info("新的地址请求注册:info:{}",info);
            rpcInfoList = new ArrayList<>();
            rpcInfoList.add(info);
            addKeyMap(key,info);
            rpcInfoMap.put(key,rpcInfoList);
        }else {
            //判断是重复注册还是 多个节点注册1个服务
            int checked = checkAddr(info, rpcInfoList);
            if(checked==0){ //是多个节点注册一个服务
                rpcInfoList.add(info);
                addKeyMap(key,info);
                logger.info("注册完成...info:{}",info);
            }else if(checked==1) { //重复连接，抛出异常!
                logger.error("同一个节点不可重复注册...");
                throw new RepeatRegisteException("不可重复注册...");
            }
        }
    }

    @Override
    public void unregiste(RpcInfo info) {

    }

    //todo 下线时，keyMap是否需要同步删除??待分析
    @Override
    public void unregiste(String host, int port) {
        String key = host+ ":" + port;
        Set<String> allKeys = keyMap.get(key);
        if(allKeys!=null) {
            for (String chkey : allKeys) {
                List<RpcInfo> rpcInfoList = rpcInfoMap.get(chkey);
                if(rpcInfoList.size()==1){
                    rpcInfoMap.remove(chkey);
                }else{
                    int delIndex =-1;
                    for (int i = 0; i <rpcInfoList.size() ; i++) {
                        RpcInfo temp = rpcInfoList.get(i);
                        if(host.equals(temp.getHost()) && port==temp.getServerToCenterPort()) {
                            delIndex = i;
                            break;
                        }
                    }

                    if(delIndex!=-1) rpcInfoList.remove(delIndex);

                }
            }
            logger.info("{} 机器已经与注册中心断开连接...", key);
        }else {
            logger.warn("找不到{} 在注册中心注册的元数据...",key);
        }
    }

    @Override
    public RpcInfo findServer(RpcInfo info, IRoute route) throws ServerNotFoundException {
        String key = generateKey(info);
        RpcInfo res = null;
        List<RpcInfo> rpcInfoList = rpcInfoMap.get(key);
        if(StringUtil.listIsEmpty(rpcInfoList)){
            logger.error("找不到对应的服务...key:{}",key);
            throw new ServerNotFoundException("找不到对应的服务...");
        }else {
            if(rpcInfoList.size()==1){ //只有1台机器，直接返回l
                res = cloneInfo(rpcInfoList.get(0));
            }else { //多台机器根据负载均衡策略进行返回
                logger.info("存在多个节点，选取负载策略...");
                logger.debug("rpcInfoList = {}",rpcInfoList);
                // TODO: 2021/11/9 暂时取第一个，后续再开发负载均衡策略模块
//                route.addAsEmpty(key);
                res = route.selectServer(rpcInfoList);
            }
        }
        return res;
    }

}
